package com.xrui.hbasecmd.hbase;

import com.google.common.base.Objects;

import com.xrui.hbase.filter.RowFilter;
import org.apache.hadoop.hbase.util.Bytes;

import java.util.Date;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * This class represents a query object containing all possible information that can be used to filter results on hbase.
 */
public class Query {

    //region Variables
    /**
     * A key to start the scan from.
     */
    private byte[] startKey;
    /**
     * A key components map to start the scan from.
     */
    private Map<String, String> startComponents;
    /**
     * A key where the scan should stop.
     */
    private byte[] endKey;
    /**
     * A key components map where the scan should stop.
     */
    private Map<String, String> endComponents;
    /**
     * The date to start the scan from. Each key/value pair in hbase has a timestamp.
     */
    private Date startDate;
    /**
     * The date to stop the scan.
     */
    private Date endDate;
    /**
     * The name of the column family to scan on.
     */
    private String family;
    /**
     * The name of the column to scan on.
     */
    private String column;
    /**
     * Holds both column family and column names.
     */
    private String columnWithFamily;
    /**
     * The max versions.
     */
    private int maxVersions;
    /**
     * The row filters.
     */
    private RowFilter rowFilter;
    //endregion

    //region Public Properties

    /**
     * Gets a start key.
     *
     * @return A start key.
     */
    public byte[] getStartKey() {
        return this.startKey;
    }

    /**
     * Sets a new start key.
     *
     * @param value A new start key.
     */
    public void setStartKey(byte[] value) {
        this.startKey = value;
    }

    /**
     * Gets an end key.
     *
     * @return An end key.
     */
    public byte[] getEndKey() {
        return this.endKey;
    }

    /**
     * Sets a new end key.
     *
     * @param value A new end key.
     */
    public void setEndKey(byte[] value) {
        this.endKey = value;
    }

    /**
     * Gets a start date.
     *
     * @return A start date.
     */
    public Date getStartDate() {
        return this.startDate;
    }

    /**
     * Sets a new start date.
     *
     * @param value A new start date.
     */
    public void setStartDate(Date value) {
        this.startDate = value;
    }

    /**
     * Gets an end date.
     *
     * @return An end date.
     */
    public Date getEndDate() {
        return this.endDate;
    }

    /**
     * Sets a new end date.
     *
     * @param value A new end date.
     */
    public void setEndDate(Date value) {
        this.endDate = value;
    }

    /**
     * Gets a column family.
     *
     * @return A column family.
     */
    public String getFamily() {
        return this.family;
    }

    /**
     * Sets a new column family.
     *
     * @param value A new column family.
     */
    public void setFamily(String value) {
        this.family = value;
    }

    /**
     * Gets a column name.
     *
     * @return A column name.
     */
    public String getColumn() {
        return this.column;
    }

    /**
     * Sets a new column name.
     *
     * @param value A new column name.
     */
    public void setColumn(String value) {
        this.column = value;
    }

    /**
     * Gets a column family and a column name concatenated as a single string.
     *
     * @return A column family and a column name represented as a single string.
     */
    public String getColumnWithFamily() {
        if (this.columnWithFamily == null) {
            this.columnWithFamily = String.format("%s:%s", this.family, this.column);
        }
        return this.columnWithFamily;
    }

    /**
     * Gets the max versions.
     *
     * @return Number of max versions.
     */
    public int getMaxVersions() {
        return this.maxVersions;
    }

    /**
     * Sets the max versions.
     *
     * @param value Number of max versions.
     */
    public void setMaxVersions(int value) {
        this.maxVersions = value;
    }

    public RowFilter getRowFilter() {
        return rowFilter;
    }

    public void setRowFilter(RowFilter rowFilter) {
        this.rowFilter = rowFilter;
    }

    public Map<String, String> getStartComponents() {
        return startComponents;
    }

    public void setStartComponents(Map<String, String> startComponents) {
        this.startComponents = startComponents;
    }

    public Map<String, String> getEndComponents() {
        return endComponents;
    }

    public void setEndComponents(Map<String, String> endComponents) {
        this.endComponents = endComponents;
    }

    public String summarize() {
        Map<String, String> startComponents = getStartComponents().entrySet().stream()
            .filter(e -> e.getValue() != null)
            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
        Map<String, String> endComponents = getEndComponents().entrySet().stream()
            .filter(e -> e.getValue() != null)
            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

        if(startComponents.size() > 0 && endComponents.size() > 0) {
            return String.format("StartKey=%s, EndKey=%s", startComponents, endComponents);
        } else if (startComponents.size() > 0) {
            return String.format("StartKey=%s", startComponents);
        } else {
            return String.format("EndKey=%s", endComponents);
        }
    }

    @Override
    public String toString() {
        return Objects.toStringHelper(Query.class)
            .add("column", columnWithFamily)
            .add("maxVersions", maxVersions)
            .add("startTime", startDate)
            .add("endTime", endDate)
            .add("startKey", Bytes.toStringBinary(startKey))
            .add("endKey", Bytes.toStringBinary(endKey))
            .add("startComponents", startComponents)
            .add("endComponents", endComponents)
            .add("filters", rowFilter)
            .toString();
    }
    //endregion
}
